package hcqs.nav.grid.map.algo;

import hcqs.nav.grid.macros.Point;

import java.util.ArrayList;
import java.util.List;

public class GridSimLine {
    private static GridSimLine gridSimLine = new GridSimLine();

    private GridSimLine() {

    }

    public static GridSimLine getInstance() {
        return gridSimLine;
    }

    private List<Point> plotLineLow(int x0, int y0, int x1, int y1, boolean needReverse) {
        List<Point> result = new ArrayList<>();
        int dx = x1 - x0;
        int dy = y1 - y0;
        int y_gain = 1;
        if (dy < 0) {
            y_gain = -1;
            dy = -dy;
        }

        int D = (dy << 1) - dx;
        int D_add1 = (dy - dx) << 1;
        int D_add0 = (dy) << 1;
        int y = y0;

        for (int x = x0; x < x1; x++) {
            Point p = new Point(x, y);
            if (needReverse) {
                result.add(0, p);
            } else {
                result.add(p);
            }
            if (D > 0) {
                y = y + y_gain;
                D += D_add1;
            } else {
                D += D_add0;
            }
        }

        Point p = new Point(x1, y1);
        if (needReverse) {
            result.add(0, p);
        } else {
            result.add(p);
        }
        return result;
    }

    private List<Point> plotLineHigh(int x0, int y0, int x1, int y1, boolean needReverse) {
        List<Point> result = new ArrayList<>();
        int dx = x1 - x0;
        int dy = y1 - y0;
        int x_gain = 1;
        if (dx < 0) {
            x_gain = -1;
            dx = -dx;
        }

        int D = (dx << 1) - dy;
        int D_add1 = (dx - dy) << 1;
        int D_add0 = (dx) << 1;
        int x = x0;

        for (int y = y0; y < y1; y++) {
            Point p = new Point(x, y);
            if (needReverse) {
                result.add(0, p);
            } else {
                result.add(p);
            }

            if (D > 0) {
                x = x + x_gain;
                D += D_add1;
            } else {
                D += D_add0;
            }
        }

        Point p = new Point(x1, y1);
        if (needReverse) {
            result.add(0, p);
        } else {
            result.add(p);
        }
        return result;
    }

    public List<Point> plotLine(int x0, int y0, int x1, int y1) {
        if (Math.abs(y1 - y0) < Math.abs(x1 - x0)) {
            if (x0 > x1) {
                return plotLineLow(x1, y1, x0, y0, true);
            } else {
                return plotLineLow(x0, y0, x1, y1, false);
            }
        } else {
            if (y0 > y1) {
                return plotLineHigh(x1, y1, x0, y0, true);
            } else {
                return plotLineHigh(x0, y0, x1, y1, false);
            }
        }
    }
}
